/***************************************************************************
 *   Copyleft 2006-2007 by												   *
 *	 Iwanicki Krzysztof													   *
 *   <gamma.mini.pw.edu.pl/~iwanickik>									   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 ***************************************************************************/
#include "./GregorySurface.h"

/**************************************************************
* Constructors                                                *
***************************************************************/
GregorySurface::GregorySurface() {
	mSurfaceColor = RGB(0, 255, 255);
	mU = 10;
	mV = 10;
	CalculateSurface();
}

GregorySurface::~GregorySurface() {
}
    
/**************************************************************
* Operators                                                   *
***************************************************************/


/**************************************************************
* Other helpful functions                                     *
***************************************************************/
/*virtual*/ cVoid GregorySurface::Render(Renderer& renderer, Matrix4D& matrix, ProjectionPlane& plane) {
	DynamicArray2D<Point4D> temp(mPolygons.GetRowSize(), mPolygons.GetColSize());
	cInt iHalfWidth = renderer.GetWidth() >> 1, iHalfHeight = renderer.GetHeight() >> 1;
	Point4D a;
	cInt x1, y1, x2, y2;

	for (cInt i = 0; i < mPolygons.GetRowSize(); i++) {
		for (cInt j = 0; j < mPolygons.GetColSize(); j++) {
			a = mPolygons(i, j);
			a.mW = 1.0f;
			temp(i, j) = matrix * mMatrix * a;
		}
	}

	for (cInt i = 0; i < mPolygons.GetRowSize(); i++) {
		for (cInt j = 0; j < mPolygons.GetColSize() - 1; j++) {
			if (temp(i, j).mW > plane.GetNearClippingPlane()
				&& temp(i, j + 1).mW > plane.GetNearClippingPlane()) {
				x1 = (cInt) (temp(i, j).mX / temp(i, j).mW) + iHalfWidth;
				y1 = (cInt) -(temp(i, j).mY / temp(i, j).mW) + iHalfHeight;
				x2 = (cInt) (temp(i, j + 1).mX / temp(i, j + 1).mW) + iHalfWidth;
				y2 = (cInt) -(temp(i, j + 1).mY / temp(i, j + 1).mW) + iHalfHeight;
				renderer.DrawROPLine(x1, y1, x2, y2, mSurfaceColor);
			}
		}
	}

	for (cInt i = 0; i < mPolygons.GetRowSize() - 1; i++) {
		for (cInt j = 0; j < mPolygons.GetColSize(); j++) {
			if (temp(i, j).mW > plane.GetNearClippingPlane() 
				&& temp(i + 1, j).mW > plane.GetNearClippingPlane()) {
				x1 = (cInt) (temp(i, j).mX / temp(i, j).mW) + iHalfWidth;
				y1 = (cInt) -(temp(i, j).mY / temp(i, j).mW) + iHalfHeight;
				x2 = (cInt) (temp(i + 1, j).mX / temp(i + 1, j).mW) + iHalfWidth;
				y2 = (cInt) -(temp(i + 1, j).mY / temp(i + 1, j).mW) + iHalfHeight;
				renderer.DrawROPLine(x1, y1, x2, y2, mSurfaceColor);
			}
		}
	}
}

/*virtual*/ cVoid GregorySurface::Render(Renderer& renderer, Matrix4D& matrix, ProjectionPlane& plane, cWinColor color) {
	DynamicArray2D<Point4D> temp(mPolygons.GetRowSize(), mPolygons.GetColSize());
	cInt iHalfWidth = renderer.GetWidth() >> 1, iHalfHeight = renderer.GetHeight() >> 1;
	Point4D a;
	cInt x1, y1, x2, y2;

	for (cInt i = 0; i < mPolygons.GetRowSize(); i++) {
		for (cInt j = 0; j < mPolygons.GetColSize(); j++) {
			a = mPolygons(i, j);
			a.mW = 1.0f;
			temp(i, j) = matrix * mMatrix * a;
		}
	}

	for (cInt i = 0; i < mPolygons.GetRowSize(); i++) {
		for (cInt j = 0; j < mPolygons.GetColSize() - 1; j++) {
			if (temp(i, j).mW > plane.GetNearClippingPlane()
				&& temp(i, j + 1).mW > plane.GetNearClippingPlane()) {
				x1 = (cInt) (temp(i, j).mX / temp(i, j).mW) + iHalfWidth;
				y1 = (cInt) -(temp(i, j).mY / temp(i, j).mW) + iHalfHeight;
				x2 = (cInt) (temp(i, j + 1).mX / temp(i, j + 1).mW) + iHalfWidth;
				y2 = (cInt) -(temp(i, j + 1).mY / temp(i, j + 1).mW) + iHalfHeight;
				renderer.DrawROPLine(x1, y1, x2, y2, color);
			}
		}
	}

	for (cInt i = 0; i < mPolygons.GetRowSize() - 1; i++) {
		for (cInt j = 0; j < mPolygons.GetColSize(); j++) {
			if (temp(i, j).mW > plane.GetNearClippingPlane() 
				&& temp(i + 1, j).mW > plane.GetNearClippingPlane()) {
				x1 = (cInt) (temp(i, j).mX / temp(i, j).mW) + iHalfWidth;
				y1 = (cInt) -(temp(i, j).mY / temp(i, j).mW) + iHalfHeight;
				x2 = (cInt) (temp(i + 1, j).mX / temp(i + 1, j).mW) + iHalfWidth;
				y2 = (cInt) -(temp(i + 1, j).mY / temp(i + 1, j).mW) + iHalfHeight;
				renderer.DrawROPLine(x1, y1, x2, y2, color);
			}
		}
	}
}

Point4D GregorySurface::CalculatePoint(cFloat u, cFloat v) {
	cFloat Hu[4], Hv[4];
	Point4D temp[4], res;
	DynamicArray2D<Point4D> H(4, 4);
	CalculateHermite(u, Hu);
	CalculateHermite(v, Hv);

	H(0,0) = Q[0][0];
	H(0,1) = Q[0][1];
	H(1,0) = Q[1][0];
	H(1,1) = Q[1][1];

	H(0,2) = Qv[0][0];
	H(0,3) = Qv[0][1];
	H(1,2) = Qv[1][0];
	H(1,3) = Qv[1][1];

	H(2,0) = Qu[0][0];
	H(2,1) = Qu[0][1];
	H(3,0) = Qu[1][0];
	H(3,1) = Qu[1][1];

	if (fabs(u + v) < 0.001f) {
		H(2,2) = (Qvu[0][0]*u + Quv[0][0]*v)/(u + v);
	}
	if (fabs(u + 1.0f - v) < 0.001f) {
		H(2,3) = (Qvu[0][1]*u + Quv[0][1]*(1.0f - v))/(u + 1.0f - v);
	}
	if (fabs(1.0f - u + v) < 0.001f) {
		H(3,2) = (Qvu[1][0]*(1.0f - u) + Quv[1][0]*v)/(1.0f - u + v);
	}
	if (fabs(2.0f - u - v) < 0.001f) {
		H(3,3) = (Qvu[1][1]*(1.0f - u) + Quv[1][1]*(1.0f - v))/(2.0f - u - v);
	}

	for (cInt i = 0; i < 4; i++) {
		for (cInt k = 0; k < 4; k++) {
			temp[i] = temp[i] + H(i, k)*Hv[k];
		}
	}

	for (cInt i = 0; i < 4; i++) {
		res = res + temp[i]*Hu[i];
	}

	return res;
}

/*virtual*/ cVoid GregorySurface::CalculateSurface() {
	mPolygons = DynamicArray2D<Point4D>(mU + 1, mU + 1);
	cFloat u = 0.0f, du = 1.0f / mU;
	cFloat v = 0.0f, dv = 1.0f / mU;

	for (cInt i = 0; i < mU + 1; i++, u+=du) {
		v = 0.0f;
		for (cInt j = 0; j < mU + 1; j++, v+=dv) {
			mPolygons(i, j) = CalculatePoint(u, v);
		}
	}
}

/*virtual*/ cVoid GregorySurface::CalculateBasis() {
}

cVoid GregorySurface::BezierToHermite() {
	for (cInt i = 0; i < 2; i++) {
		for (cInt j = 0; j < 2; j++) {
			Q[i][j] = P[i][j];
		}
	}

	Qu[0][0] = (Pu[0][0] - P[0][0])*3.0f;
	Qu[0][1] = (Pu[0][1] - P[0][1])*3.0f;
	Qu[1][0] = (Pu[1][0] - P[1][0])*-3.0f;
	Qu[1][1] = (Pu[1][1] - P[1][1])*-3.0f;

	Qv[0][0] = (Pv[0][0] - P[0][0])*3.0f;
	Qv[1][0] = (Pv[1][0] - P[1][0])*3.0f;
	Qv[0][1] = (Pv[0][1] - P[0][1])*-3.0f;
	Qv[1][1] = (Pv[1][1] - P[1][1])*-3.0f;

	Quv[0][0] = (Puv[0][0] - P[0][0] - Qu[0][0]/3.0f - Qv[0][0]/3.0f)*9.0f;
	Quv[0][1] = (Puv[0][1] - P[0][1] - Qu[0][1]/3.0f + Qv[0][1]/3.0f)*9.0f;
	Quv[1][1] = (Puv[1][1] - P[1][1] + Qu[1][1]/3.0f + Qv[1][1]/3.0f)*9.0f;
	Quv[1][0] = (Puv[1][0] - P[1][0] + Qu[1][0]/3.0f - Qv[1][0]/3.0f)*9.0f;

	Qvu[0][0] = (Pvu[0][0] - P[0][0] - Qu[0][0]/3.0f - Qv[0][0]/3.0f)*9.0f;
	Qvu[0][1] = (Pvu[0][1] - P[0][1] - Qu[0][1]/3.0f + Qv[0][1]/3.0f)*9.0f;
	Qvu[1][1] = (Pvu[1][1] - P[1][1] + Qu[1][1]/3.0f + Qv[1][1]/3.0f)*9.0f;
	Qvu[1][0] = (Pvu[1][0] - P[1][0] + Qu[1][0]/3.0f - Qv[1][0]/3.0f)*9.0f;
}

cVoid GregorySurface::CalculateHermite(cFloat t, cFloat *out) {
	cFloat b10, b20, b21, b22, b30, b31, b32, b33;

	b10 = 1.0f - t;
	b20 = b10*b10;
	b21 = 2.0f*t*b10;
	b22 = t*t;
	b30 = b10*b20;
	b31 = t*b20 + b10*b21;
	b32 = t*b21 + b10*b22;
	b33 = t*b22;

	out[0] = b30 + b31;
	out[1] = b32 + b33;
	out[2] = b31/3.0f;
	out[3] = -b32/3.0f;
}

/*virtual*/ cChar* GregorySurface::GetName() {
	return "GregorySurface";
}

/*virtual*/ cVoid GregorySurface::DisplayInfo(Renderer& renderer) {
}
